/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */

package cn.ac.nercis.pes.service.document;

import cn.ac.nercis.pes.common.event.ProgressEvent;
import cn.ac.nercis.pes.common.listener.ProgressListener;
import cn.ac.nercis.pes.common.utils.JsonUtils;
import cn.ac.nercis.pes.model.bo.doc.ReportDataBO;
import cn.ac.nercis.pes.model.bo.doc.common.DocRequest;
import cn.ac.nercis.pes.model.bo.doc.common.evaluation.DocMessageType;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.common.ResultCode;
import cn.ac.nercis.pes.repository.dal.project.SystemEvaluationRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemRepository;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import cn.ac.nercis.pes.repository.model.project.SystemEvaluationDO;
import cn.ac.nercis.pes.service.document.common.DocDataService;
import cn.ac.nercis.pes.service.document.report.*;
import cn.ac.nercis.pes.service.utils.DocServerUtils;
import cn.ac.nercis.pes.service.utils.FileManage;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.Objects;

/**
 * 密评报告生成服务类
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public class PesReportService {
    private final static ProgressEvent PROGRESS_EVENT = new ProgressEvent("密码测评报告");

    private final ReportDataService dataService;
    private final DocDataService docDataService;
    private final SystemRepository systemRepository;
    private final SystemEvaluationRepository systemEvaluationRepository;
    private final ReportOverallEvaluationDataService overallEvaluationDataService;
    private final ReportUnitEvaluationDataService reportUnitEvaluationDataService;
    private final ReportRecordDataService recordDataService;
    private final ReportProofDataService reportProofDataService;

    private ProgressListener progressListener;

    @Autowired
    public PesReportService(ReportDataService dataService,
                            DocDataService docDataService,
                            SystemRepository systemRepository,
                            SystemEvaluationRepository systemEvaluationRepository,
                            ReportOverallEvaluationDataService overallEvaluationDataService,
                            ReportUnitEvaluationDataService reportUnitEvaluationDataService,
                            ReportRecordDataService recordDataService,
                            ReportProofDataService reportProofDataService){
        this.dataService = dataService;
        this.docDataService = docDataService;
        this.systemRepository = systemRepository;
        this.systemEvaluationRepository = systemEvaluationRepository;
        this.overallEvaluationDataService = overallEvaluationDataService;
        this.reportUnitEvaluationDataService = reportUnitEvaluationDataService;
        this.recordDataService = recordDataService;
        this.reportProofDataService = reportProofDataService;

        this.progressListener = (event, args) -> {};
    }

    /**
     * 设置事件通知处理方法
     * @param event 事件处理方法
     */
    public final void setOnProgress(ProgressListener event){
        if(Objects.nonNull(event)){
            this.progressListener = event;
        }
    }

    /**
     * 生成密评报告
     * @param systemId 系统Id
     * @param filePath 报告文件存储路径
     * @return 报告生成结果
     */
    public Result<String> generateReport(@NonNull String systemId, File filePath){
        if(!DocServerUtils.testConnect()){
            return Result.failed("文档生成服务未启动，无法生成密评报告！");
        }
        SystemDO systemDO = systemRepository.findById(systemId).orElse(null);
        if(Objects.isNull(systemDO)){
            return Result.failed(ResultCode.SYSTEM_NOT_EXIST);
        }
        var evaluationDO = systemEvaluationRepository.findBySystemId(systemId).orElse(null);
        if(Objects.isNull(evaluationDO)){
            return Result.failed("缺少系统测评基础信息！");
        }
        try{
            FileManage.createDirectory(filePath.getParentFile());
            DocRequest request = DocRequest.builder()
                    .messageType(DocMessageType.PES_REPORT.getType())
                    .savePath(filePath.getAbsolutePath())
                    .build();
            var reportData = buildReportData(systemDO,evaluationDO);
            request.setContent(JsonUtils.toJson(reportData));
            processNotify(0.9,"即将生成完成...");
            return DocServerUtils.generateDocument(request);
        }catch (Exception ex){
            log.error("密评报告生成错误：",ex);
            return Result.failed(ex.getMessage());
        }
    }



    private ReportDataBO buildReportData(SystemDO systemDO, SystemEvaluationDO evaluationDO) throws Exception{
        ReportDataBO reportData = new ReportDataBO();
        processNotify(0.1);
        reportData.setReportCode(dataService.getReportCode(systemDO));
        reportData.setTestedUnit(dataService.getTestedUnit(systemDO));
        reportData.setTestedUnitSystemName(dataService.getTestedUnitSystemName(systemDO));
        reportData.setEntrustedUnitName(dataService.getEntrustedUnitName(systemDO));
        reportData.setHasEntrustedUnitName(StringUtils.isNotBlank(reportData.getEntrustedUnitName())
                && (!StringUtils.equals(reportData.getEntrustedUnitName(),reportData.getTestedUnit().getName())));
        reportData.setReportDate(docDataService.getReportDate(systemDO));
        reportData.setReportDateInDay(dataService.getReportDateInDay(systemDO));
        reportData.setDistributionRange(dataService.getDistributionRange(systemDO));

        processNotify(0.2);
        reportData.setEvaluationUnit(dataService.getEvaluationUnit());
        reportData.setSystem(dataService.getSystem(systemDO,evaluationDO));
        reportData.setGradeProtection(dataService.getGradeProtection(evaluationDO));
        reportData.setServiceCondition(dataService.getServiceCondition(evaluationDO));
        reportData.setServiceArea(dataService.getServiceArea(evaluationDO));
        reportData.setNetworkPlatform(dataService.getNetworkPlatform(evaluationDO));
        reportData.setRunCondition(dataService.getRunCondition(evaluationDO));
        reportData.setInterconnection(dataService.getInterconnection(evaluationDO));
        reportData.setCloudSystem(dataService.getCloudSystem(evaluationDO));
        reportData.setPaScheme(dataService.getPAScheme(evaluationDO));
        reportData.setPpSituation(dataService.getPPSituation(evaluationDO));
        reportData.setUseAlgorithm(dataService.getUseAlgorithm(evaluationDO));

        processNotify(0.4);
        reportData.setTargetStat(overallEvaluationDataService.getTargetStat(systemDO));
        reportData.setProblemSuggest(overallEvaluationDataService.getProblemSuggest(systemDO));
        reportData.setEvaluationBasis(docDataService.getEvaluationBasis(systemDO));
        reportData.setEvaluationProcess(docDataService.getEvaluationProcess(systemDO));
        reportData.setEvaluators(docDataService.getEvaluatorTable(systemDO));
        reportData.setSystemSituation(dataService.getSystemSituation(systemDO));
        reportData.setEvaluationRange(docDataService.getEvaluationRange(systemDO));

        processNotify(0.5);
        reportData.setUnitEval(reportUnitEvaluationDataService.getUnitEvaluationData(systemDO));

        processNotify(0.6);
        reportData.setOverallEval(overallEvaluationDataService.getOverallEvalData(systemDO));

        processNotify(0.7);
        reportData.setRiskAnalysis(overallEvaluationDataService.getRiskAnalysisData(systemDO));
        reportData.setTotalScore(overallEvaluationDataService.getOverallScore(systemDO));
        reportData.setEvaluationResult(reportData.getRiskAnalysis().getEvalResult());

        processNotify(0.8);
        reportData.setAppendix(recordDataService.getEvaluationRecordData(systemDO));
        reportData.setAppendixB(reportProofDataService.getAppendixB(systemDO));
        return reportData;
    }

    private void processNotify(double value){
        processNotify(value,"");
    }

    private void processNotify(double value,String message){
        progressListener.notification(PROGRESS_EVENT, ProgressEvent.ProgressArgs.builder()
                .value(value)
                .message(message)
                .build());
    }
}
